// https://www.bilibili.com/video/BV1Xy4y1v7S2/?p=16&spm_id_from=pageDriver&vd_source=0edc6e41570cd7a1d7dae6d34d5bd0b3
// 类和属性已经方法的定义和使用
class Person {

    // 定义实例属性,直接通过实例进行访问
    name: string = "黄忠"
    age: number = 18
    // 定义静态属性,只能通过类进行访问
    static address: string = "南山区"
    // 只读属性的属性
    readonly idCard: number = 13579

    // 属性方法定义,这里定义不需要写function()
    sayHello() {
        return "末将" + this.name + "今年" + this.age.toString()
    }
    // 类方法,也就是使用 static 定义的,可以认为是静态方法,只能用类名进行访问,而且类方法里面只能调用静态属性
    static watchTV() {
        // return this.name + "在看电视"
        return "我的地址是" + this.address
    }
}

const per = new Person()

console.log(per.name)
console.log(per.age)

// console.log(per.adderss) // 这里的静态属性不可以使用实例进行访问,只能使用类进行访问
console.log(Person.address)

per.name = "赵子龙"
console.log(per.name)

Person.address = "吴国"
console.log(Person.address)

console.log(per.idCard)
// per.idCard = 145  // 这里只读属性是不允许改变的

console.log(per.sayHello()) // 这里就可以直接通过实例来访问方法

console.log(Person.watchTV()) // 调用类方法,类方法也就只能调用静态属性


// 构造函数(感觉这个定义和c++差不多啊,搞的都是老一套了)
class Dog {
    name = "旺财"
    age = 2
    bark () {
        console.log('汪汪汪')
        // alert("ddd") // 这个ts编译不报错,但是编译成js以后无法运行
    }
}
const d1 = new Dog()
d1.bark()

// 这个时候定义类的时候创建属性就需要赋值

class Cat {
    name:string
    age:number
}
const c1 = new Cat()
console.log(c1.name) // 这里没有赋值的时候直接log是 undefined
c1.name = "小喵"
console.log(c1.name) // 这里赋值以后就可以显示了

class Cat2 {
    name:string
    age:number
    // 构造函数
    constructor(n:string, a:number) {
        this.name = n
        this.age = a
    }
}
const cc1 = new Cat2("大黄", 1) // 这里定义构造函数以后,new的时候就需要赋值了
console.log(cc1.name, cc1.age)

// -------------------- 继承的了解 --------------
class Animal {
    name:string
    age:number
    constructor(n:string, a:number) {
        this.name = n
        this.age = a
    }
    sayHello () {
        console.log('动物在叫')
    }
}

// 这里 BigDog 继承 Animal 的类
class BigDog extends Animal {
    sayHello() { // 重写父类
        // super.sayHello();
        console.log("汪汪汪")
    }
}

class BigCat extends Animal {
    address: string
    sayHello() {
        // 这里调用子类的这个方法,如果没有的时候,就会调用父类的这个方法
        super.sayHello();
    }
    // 这里如果子类重写父类的构造函数,那么就必须调用父类的构造函数,否则会被替换掉,父类对于其他值的初始化不生效会导致出现问题
    constructor(n:string, a:number, s:string) {
        super(n, a);// 子类重写父类的构造函数,就必须调用父类的构造函数
        this.address = s
    }

}

// const b1 = new BigDog()
// b1.sayHello() // 这里继承以后,子类就可以调用父类的方法和属性
const b2 = new BigCat("小黑", 1,"狗窝")
console.log(b2.name,b2.age,b2.address)

// ------------------------------ 抽象类 ----------------------------------
// 抽象类, 上面在继承的时候,Animal 也可以被实例化进行使用,但是在开发中,通常不希望这样的父类被实例化,
// 所以可以使用 abstract 定义为抽象类,这样就让这个类不会被创建对象,只能被继承使用

abstract class MoreAnimal {
    name:string
    age:number
    constructor(n:string, a:number) {
        this.name = n
        this.age = a
    }
    sayHello () {
        console.log('动物在叫')
    }
    // 这里定义抽象方法,不进行实现,继承者必须实现这个方法
    abstract sayKitty():void // 这里定义了以后,继承者不实现这个方法就会报错
}

class MoreDog extends MoreAnimal {
    // 这里必须实现父类定义的抽象方法
    sayKitty() {
        console.log("狗子在叫")
    }
}

class Corgi extends MoreDog {
    // 这里因为在爷爷类定义的抽象函数,在父类重写了,这里不会报错,这里如果不重写的话,实际上就是继承了父类的这个方法
    sayKitty() {
        // super.sayKitty();
        console.log('柯基在叫')
    }
}

const d2 = new MoreDog("田园犬",1)
console.log(d2.name)
d2.sayHello() // 这里虽然也可以调用抽象父类里面定义的构造函数和方法,但是通常我们不希望直接调用父类的方法
d2.sayKitty()

const d3 = new Corgi("毛毛",2)
d3.sayKitty() // 这里调用就是重写以后的方法

// --------------------- 接口的概念 -------------

// 1 接口用来定义一个类的结构
interface myInterface {
    name:string
    age:number
}

// 这样在定义类的时候,就必须有接口里面的属性和方法
const obj: myInterface = {
    name: "张莎",
    age: 18,
    address: "fasdf" // 后面添加一个接口属性以后,这里也就需要补上这个属性
}

// 接口还可以多个定义一起作用
interface myInterface {
    address:string
}

// 2 接口还可以在定义类的时候去限制类的结构,接口和抽象类类似,但是接口里面的方法都是抽象方法
interface myInter {
    name:string
    sayHello():void;
}

// 定义接口用 interface 而实现接口用的是 implement
class MyClass implements myInter {
    name: string; // 类实现接口要实现接口里面定义的所有的属性和方法

    sayHello(): void {
    }

}

// 3 属性的封装
// 背景,因为属性可以任意修改,会导致逻辑错误,例如说年龄改成 -3
class Book {
    _name: string
}

// 新建一个 interface

// 参数: 特殊门禁申请
export interface GetSpecialAuthAppllyParam {
    tag_ids: string[]; // 标签id列表
    apply_desc: string; // 申请说明
    data_file?: string; // 上传附件
    group_list: GetSpecialAuthAppllyGroupList[]; // 分组请求信息
}

export interface GetSpecialAuthAppllyGroupList {
    staff_name: string[]; // 人员选择
    date_range_start: string; // 授权生效日期
    date_range_end: string; // 授权失效日期
    apply_reason: string; // 门禁需求说明
}

// let cardList: GetSpecialAuthAppllyGroupList[{}]
// cardList = [{
//     staff_name: '',
//     date_range_start: '',
//     date_range_end: '',
//     apply_reason: ''
// }]

// 新建一个
// let params: GetSpecialAuthAppllyParam
// params.tag_ids = []
// params.apply_desc = ''
// params.group_list = cardList
//
//
//
// console.log("最后的参数", params)

class useGroup implements GetSpecialAuthAppllyGroupList{
    apply_reason: string;
    date_range_end: string;
    date_range_start: string;
    staff_name: string[];
    // 构造函数初始化属性
    constructor(a:string, s:string,e:string,name:string[]) {
        this.apply_reason = a
        this.date_range_start = s
        this.date_range_end = e
        this.staff_name = name
    }
}

class useParams implements GetSpecialAuthAppllyParam {
    apply_desc: string;
    group_list: GetSpecialAuthAppllyGroupList[];
    tag_ids: string[];
    // 构造函数
    constructor(a:string, g:GetSpecialAuthAppllyGroupList[],t:string[]) {
        this.apply_desc = a
        this.group_list = g
        this.tag_ids = t
    }
}

let groupList = new useGroup('','','',[])

console.log(groupList)

// 声明对象数组
let groupArr:useGroup[]
// 把对象数组初始化
groupArr = []
// 把对象放到数组里面
groupArr.push(groupList)
console.log(groupArr)

let params = new useParams('',groupArr,[])
console.log("显示数据", params)

// 这里定义的接口和初始化对象使用,貌似不太好用,前端只是定义一个数据结构,并不想很复杂的用来做什么,
// 看  https://gitee.com/makunet/maku-admin.git 项目看不太懂,还是要学习一下基础,前端的东西也是很复杂的

// 类内属性私有化
class MyCar {
    private _oil:number
    private _water:number

    getOil() {
        return this._oil;
    }

    setOil(o:number) {
        if (o >0) {
            if (isNaN(this._oil)) {
                this._oil = o
            } else {
                this._oil = this._oil + o
            }
        }
    }
}

let kk = new MyCar()
console.log(kk.getOil())
kk.setOil(1)
kk.setOil(-1) // 这里通过 set 方法设置加油不能为负数
kk.setOil(-2)
console.log(kk.getOil()) // 1
kk.setOil(5)
console.log(kk.getOil())
// 这里的搞法和c++类似

// 项目请求把参数设置为私有属性,无法传递使用,还是要看看具体应该使用什么方式来调用


// 泛型
// 一个函数接收一个参数,然后又返回这个参数
// function fn(a: number) :number {
//     return a
// }
// console.log(fn(9))
// 在定义函数或者是类的时候,如果遇到类型不明确的可以使用泛型
// 这里新增一个类型 T, 使用 <> 来指定这个类型
function fn<T>(a:T):T {
    return  a
}

// 同时也可以指定泛型,下面指定了string类型的泛型,看起来就是为了避免使用any导致类型判断失效带来的不好的结果,所以定义了一个多类型的类型
let result = fn<string>("hello");

